// CSVReader.cpp: implementation of the CCSVParser class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <pgl/algotools/CSVReader.h>
#include <io.h>
#include <fcntl.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

namespace parsers
{

LPCTSTR CCSVParser::m_szNumbers=_T("0.-123456789");

CCSVParser::CCSVParser()
:m_sDelims(_T(" \t,;:/\\"))
{
}

CCSVParser::~CCSVParser()
{

}


bool CCSVParser::ParseFile( LPCTSTR szFileName )
{
	bool result=false;
	TCHAR* pBuffer=NULL;
	int hFile=-1;
	long fileSize=-1;
	int nRead;
	
	// clearing vectors
	m_vectors.clear();

	// check if file can be read...
	if (_taccess( szFileName, 04 ) == -1)
		return false;

	// open file...
	hFile = _topen( szFileName, _O_RDONLY);
	if (hFile == -1)
		goto Cleaning;

	// getting file size
	fileSize =  _filelength( hFile );
	if (fileSize == -1)
		goto Cleaning;

	// creating buffer
	pBuffer =new TCHAR[fileSize+2];
	if (!pBuffer)
		goto Cleaning;

	// reading...
	nRead = _read( hFile, pBuffer, fileSize);
	if (nRead == 0)
		goto Cleaning;
	// adding end of file indicator...
	pBuffer[fileSize]=0;
	pBuffer[fileSize+1]=0;

	// parsing as string 
	result = ParseString( static_cast<LPCTSTR>(pBuffer));

Cleaning:
	if (fileSize == -1)
		_close(hFile);
	if (pBuffer)
		delete[] pBuffer;

	return result;
}

LPCTSTR CCSVParser::ReadHeaders( LPCTSTR szLine)
{
	size_t length,index=0;
	long nRead=0;
	TCHAR szBuffer[1024]={0};
	size_t nchar=0;

	// get string length...
	// if szNextLine == NULL, it's the last line.
	LPCTSTR szNextLine = _tcschr( szLine, '\n');
	if (!szNextLine)
		szNextLine =szLine+_tcslen(szLine);

	while ( length=_tcslen(szLine) > 0 && szLine < szNextLine)
	{
		// find delim
		nchar=_tcscspn(szLine, m_sDelims);
		// if not found take the entire string
		if (!nchar)
			nchar = _tcslen(szLine);
		// make sure it's one line
		nchar=algotools::min<size_t>(nchar, szNextLine-szLine);

		// copy string
		_tcsncpy( szBuffer, szLine, nchar );
		szBuffer[nchar]='\0';
		// add to current vector
		m_vectors.push_back(NamedVector(szBuffer,Vector()));

		// advancing to next delimiter
		szLine=_tcspbrk(szLine, m_sDelims);
		if (szLine >= szNextLine)
			break;
		szLine=_tcsinc(szLine);
		if (!_istalpha(szLine[0]))
			szLine=_tcsspnp(szLine, _T("\t "));
	}

	return (szNextLine) ? _tcsinc(szNextLine) : NULL;
};

LPCTSTR CCSVParser::ReadLine( LPCTSTR szLine)
{
	size_t index;
	float f;
	LPCTSTR szNextNumber;

	ASSERT(szLine);

	// get string length...
	// if szNextLine == NULL, it's the last line.
	LPCTSTR szNextLine = _tcschr( szLine, '\n');
	if (!szNextLine)
		szNextLine =szLine+_tcslen(szLine);

	// scan for next number
	index=0;
	szLine=_tcspbrk(szLine, m_szNumbers);
	while ( index < m_vectors.size() && _tcslen(szLine) > 0 && (szLine < szNextLine))
	{
		// try to read number...
		if (_stscanf(szLine,_T("%f"),&f) == 1)
			m_vectors[index].second.push_back(f);
		else
			m_vectors[index].second.push_back(0);

		++index;

		// scan delimitators...
		szLine=_tcspbrk(szLine, m_sDelims);
		if (szLine)
		{
			// checking if end of line was crossed
			if(szLine>=szNextLine)
					break;
			// find next number
			szNextNumber=_tcspbrk(szLine, m_szNumbers);
			if (szNextNumber)
			{
				// check if it is in the next line, if not advancing line
				if (szNextNumber<szNextLine)
					szLine = szNextNumber;
				else
					break;
			}
		}
	}

	// filling up vector with 0s
	for (;index<m_vectors.size();++index)
		m_vectors[index].second.push_back(0);

	return (szNextLine) ? _tcsinc(szNextLine) : NULL;
};

bool CCSVParser::ParseString( LPCTSTR szString )
{
	bool result = false;
	long nRead=0;
	LPCTSTR sz=szString;

	// clearing vectors...
	m_vectors.clear();

	////////////////////////////////////////////
	// getting number of columns...
	szString=ReadHeaders( szString );

	while (szString && _tcslen(szString)>0)
	{
		szString=ReadLine(szString);
	}

	return true;
}

void CCSVParser::ToString(CString& str, LPCTSTR szEOL)
{
	CString format;
	size_t i,j;
	const size_t vsize=m_vectors.size();
	const size_t vlinesize=(vsize) ? m_vectors[0].second.size() : 0;

	str.Empty();

	for (j = 0;j< vsize;++j)
	{
		str+=m_vectors[j].first;
		if(j<vsize-1)
			str+=_T(", ");
		else
			str+=szEOL;
	}
	
	for (i = 0;i<vlinesize;++i)
	{
		for (j = 0;j<vsize;++j)
		{
			format.Format(_T("%g"),m_vectors[j].second[i]);
			str+=format;
			if(j<vsize-1)
				str+=_T(", ");
			else
				str+=szEOL;
		}
	}
}
};